using System.Globalization;
using System.Text;
using System.Xml.Linq;
using Game;
using XmlUtilities;

namespace SCIENEW {
    public static class IECraftingRecipesManager {

        #region 实际作用方法
        public static void Init() {
            ScalingRecipes(CraftingRecipesManager.m_recipes); //将原版3x3配方扩容至6x6
            LoadIERecipes();
            ResetRecipaediaScreens();
        }

        public static void ResetRecipaediaScreens() {
            ScreensManager.m_screens["RecipaediaRecipes"] = new IERecipaediaRecipesScreen();
            ScreensManager.m_screens["Recipaedia"] = new IERecipaediaScreen();
        }

        static void LoadIERecipes() {
            XElement? ieRecipes = null;
            IndustrialModLoader.GetFile("IndustrialMod.icr", stream => { ieRecipes = XmlUtils.LoadXmlFromStream(stream, Encoding.UTF8, true); });
            if (ieRecipes != null) LoadIECRData(ieRecipes);
        }

        #endregion

        #region 实用静态方法

        /// <summary>
        /// 将3x3配方缩放至6x6
        /// </summary>
        /// <param name="recipes"></param>
        static void ScalingRecipes(List<CraftingRecipe> recipes) {
            foreach (var recipe in recipes) {
                var old = recipe.Ingredients;
                if (old.Length < 36) {
                    var ingredients2 = new string[36];
                    ingredients2[0] = old[0];
                    ingredients2[1] = old[1];
                    ingredients2[2] = old[2];
                    ingredients2[6] = old[3];
                    ingredients2[7] = old[4];
                    ingredients2[8] = old[5];
                    ingredients2[12] = old[6];
                    ingredients2[13] = old[7];
                    ingredients2[14] = old[8];
                    recipe.Ingredients = ingredients2;
                }
            }
        }

        /// <summary>
        /// 解析.icr文件的合成配方
        /// </summary>
        /// <param name="item"></param>
        public static void LoadIECRData(XElement item) {
            if (ModsManager.HasAttribute(item, (name) => { return name == "Result"; }, out XAttribute xAttribute) == false) {
                foreach (XElement xElement in item.Elements()) {
                    LoadIECRData(xElement);
                }
                return;
            }
            bool flag = false;
            ModsManager.HookAction(
                "OnCraftingRecipeDecode",
                modLoader => {
                    modLoader.OnCraftingRecipeDecode(CraftingRecipesManager.m_recipes, item, out flag);
                    return flag;
                }
            );
            if (flag == false) {
                CraftingRecipe craftingRecipe = DecodeElementToIECraftingRecipe(item);
                CraftingRecipesManager.m_recipes.Add(craftingRecipe);
            }
        }

        /// <summary>
        /// 解析.icr文件中单条合成配方的信息
        /// </summary>
        /// <param name="item"></param>
        /// <param name="HorizontalLen"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public static CraftingRecipe DecodeElementToIECraftingRecipe(XElement item, int HorizontalLen = 6) {
            var craftingRecipe = new CraftingRecipe { Ingredients = new string[36] };
            string attributeValue = XmlUtils.GetAttributeValue<string>(item, "Result");
            string desc = XmlUtils.GetAttributeValue<string>(item, "Description");
            if (desc.StartsWith("[")
                && desc.EndsWith("]")
                && LanguageControl.TryGetBlock(attributeValue, "CRDescription:" + desc.Substring(1, desc.Length - 2), out var r))
                desc = r;
            craftingRecipe.ResultValue = DecodeIEResult(attributeValue);
            craftingRecipe.ResultCount = XmlUtils.GetAttributeValue<int>(item, "ResultCount");
            string attributeValue2 = XmlUtils.GetAttributeValue(item, "Remains", string.Empty);
            if (!string.IsNullOrEmpty(attributeValue2)) {
                craftingRecipe.RemainsValue = DecodeIEResult(attributeValue2);
                craftingRecipe.RemainsCount = XmlUtils.GetAttributeValue<int>(item, "RemainsCount");
            }
            craftingRecipe.RequiredHeatLevel = XmlUtils.GetAttributeValue<float>(item, "RequiredHeatLevel");
            craftingRecipe.RequiredPlayerLevel = XmlUtils.GetAttributeValue(item, "RequiredPlayerLevel", 1f);
            craftingRecipe.Description = desc;
            craftingRecipe.Message = XmlUtils.GetAttributeValue<string>(item, "Message", null);
            if (craftingRecipe.ResultCount > BlocksManager.Blocks[Terrain.ExtractContents(craftingRecipe.ResultValue)].GetMaxStacking(craftingRecipe.ResultValue)) {
                throw new InvalidOperationException($"In recipe for \"{attributeValue}\" ResultCount is larger than max stacking of result block.");
            }
            if (craftingRecipe.RemainsValue != 0
                && craftingRecipe.RemainsCount > BlocksManager.Blocks[Terrain.ExtractContents(craftingRecipe.RemainsValue)].GetMaxStacking(craftingRecipe.RemainsValue)) {
                throw new InvalidOperationException($"In Recipe for \"{attributeValue2}\" RemainsCount is larger than max stacking of remains block.");
            }
            var dictionary = new Dictionary<char, string>();
            foreach (XAttribute item2 in from a in item.Attributes() where a.Name.LocalName.Length == 1 && char.IsLower(a.Name.LocalName[0]) select a) {
                DecodeIEIngredient(item2.Value, out string craftingId, out int? data);
                if (FindBlocksByCraftingId(craftingId).Length == 0) {
                    throw new InvalidOperationException($"Block with craftingId \"{item2.Value}\" not found.");
                }
                if (data.HasValue
                    && (data.Value < 0 || data.Value > 262143)) {
                    throw new InvalidOperationException($"Data in recipe ingredient \"{item2.Value}\" must be between 0 and 0x3FFFF.");
                }
                dictionary.Add(item2.Name.LocalName[0], item2.Value);
            }
            string[] array = item.Value.Trim().Split(new string[] { "\n" }, StringSplitOptions.None);
            for (int i = 0; i < array.Length; i++) {
                int num = array[i].IndexOf('"');
                int num2 = array[i].LastIndexOf('"');
                if (num < 0
                    || num2 < 0
                    || num2 <= num) {
                    throw new InvalidOperationException("Invalid recipe line.");
                }
                string text = array[i].Substring(num + 1, num2 - num - 1);
                for (int j = 0; j < text.Length; j++) {
                    char c = text[j];
                    if (char.IsLower(c)) {
                        string text2 = dictionary[c];
                        craftingRecipe.Ingredients[j + (i * HorizontalLen)] = text2;
                    }
                }
            }
            return craftingRecipe;
        }

        /// <summary>
        /// 解析新合成配方中的产物信息
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        public static int DecodeIEResult(string result) {
            bool flag2 = false;
            int result2 = 0;
            ModsManager.HookAction(
                "DecodeResult",
                modLoader => {
                    result2 = modLoader.DecodeResult(result, out flag2);
                    return flag2;
                }
            );
            if (flag2) return result2;
            if (!string.IsNullOrEmpty(result)) {
                if (result.Contains('-')) {
                    string[] array = result.Split("-"); //bug: 这里改用字符分割会有bug，不知道为啥
                    string blockMark = array[0], blockID = array[1];
                    switch (blockMark) {
                        case "I": return ItemsBlockManager.GetItemID(blockID);
                        case "N": return NormalBlockManager.GetNormalBlockID(blockID);
                        case "D": return DevicesBlockManager.GetDeviceValue(blockID, 0);
                        case "H": return DamageableItemManager.GetItemID(blockID);
                    }
                }
                else {
                    string[] idAndData = result.Split(new[] { ':' }, StringSplitOptions.None);
                    Block block = BlocksManager.FindBlockByTypeName(idAndData[0], throwIfNotFound: true);
                    return Terrain.MakeBlockValue(block.BlockIndex, 0, data: (idAndData.Length == 2) ? int.Parse(idAndData[1], CultureInfo.InvariantCulture) : 0);
                }
            }
            return 0;
        }

        /// <summary>
        /// 解析新合成配方中的原料信息
        /// </summary>
        /// <param name="ingredient"></param>
        /// <param name="craftingId"></param>
        /// <param name="data"></param>
        public static void DecodeIEIngredient(string ingredient, out string craftingId, out int? data) {
            bool flag2 = false;
            string craftingId_R = string.Empty;
            int? data_R = null;
            ModsManager.HookAction(
                "DecodeIngredient",
                modLoader => {
                    modLoader.DecodeIngredient(ingredient, out craftingId_R, out data_R, out flag2);
                    return flag2;
                }
            );
            if (flag2) {
                craftingId = craftingId_R;
                data = data_R;
                return;
            }
            string[] array = ingredient.Split(new[] { ':' }, StringSplitOptions.None);
            craftingId = array[0];
            data = (array.Length >= 2) ? new int?(int.Parse(array[1], CultureInfo.InvariantCulture)) : null;
        }

        /// <summary>
        /// 依据方块合成id获取方块数字id
        /// </summary>
        /// <param name="craftingId"></param>
        /// <returns></returns>
        public static int[] FindBlocksByCraftingId(string craftingId) {
            //原版
            List<int> blocks = [];
            if (!craftingId.Contains('-')) {
                foreach (var c in BlocksManager.Blocks) {
                    if (c.MatchCrafingId(craftingId)) blocks.Add(c.BlockIndex);
                }
            }
            else {
                //IE
                string mark = craftingId.Split('-')[0];
                string id = craftingId.Split('-')[1];
                if (mark == "I") blocks.Add(ItemsBlockManager.GetItemID(id));
                if (mark == "N") blocks.Add(NormalBlockManager.GetNormalBlockID(id));
                if (mark == "D") blocks.Add(DevicesBlockManager.GetDeviceValue(id, 0));
                if (mark == "H") blocks.Add(DamageableItemManager.GetItemID(id));
            }
            return blocks.ToArray();
        }

        /// <summary>
        /// 两个配方是否匹配
        /// </summary>
        /// <param name="requiredIngredients"></param>
        /// <param name="actualIngredients"></param>
        /// <returns></returns>
        public static bool MatchRecipe(string[] requiredIngredients, string[] actualIngredients) {
            bool flag2 = false;
            bool result = false;
            ModsManager.HookAction(
                "MatchRecipe",
                modLoader => {
                    result = modLoader.MatchRecipe(requiredIngredients, actualIngredients, out flag2);
                    return flag2;
                }
            );
            if (flag2) return result;
            if (actualIngredients.Length > 36) return false;
            string[] array = new string[36];
            for (int i = 0; i < 2; i++) {
                for (int j = -6; j <= 6; j++) {
                    for (int k = -6; k <= 6; k++) {
                        bool flip = i != 0;
                        if (!TransformRecipe(
                                array,
                                requiredIngredients,
                                k,
                                j,
                                flip
                            )) {
                            continue;
                        }
                        bool flag = true;
                        for (int l = 0; l < 36; l++) {
                            if (l == actualIngredients.Length
                                || !CompareIngredients(array[l], actualIngredients[l])) {
                                flag = false;
                                break;
                            }
                        }
                        if (flag) {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// 平行移动整个配方原料
        /// </summary>
        /// <param name="transformedIngredients"></param>
        /// <param name="ingredients"></param>
        /// <param name="shiftX"></param>
        /// <param name="shiftY"></param>
        /// <param name="flip"></param>
        /// <returns></returns>
        public static bool TransformRecipe(string[] transformedIngredients, string[] ingredients, int shiftX, int shiftY, bool flip) {
            // 初始化 transformedIngredients 数组
            Array.Clear(transformedIngredients, 0, transformedIngredients.Length);

            int baseIndex = 0;  // 基本索引，用于计算位置

            for (int j = 0; j < 6; j++) {
                int num2 = j + shiftY;
                int destIndex = num2 * 6; // 每行的起始索引

                if (num2 < 0 || num2 >= 6) {
                    // num2 超出范围，跳过当前行
                    for (int k = 0; k < 6; k++) {
                        if (!string.IsNullOrEmpty(ingredients[baseIndex + k])) {
                            return false;
                        }
                    }
                    baseIndex += 6; // 移动到下一行
                    continue;
                }

                for (int k = 0; k < 6; k++) {
                    int num = (flip ? (5 - k) : k) + shiftX;
                    if (num >= 0 && num < 6) {
                        transformedIngredients[num + destIndex] = ingredients[baseIndex + k];
                    }
                    else if (!string.IsNullOrEmpty(ingredients[baseIndex + k])) {
                        return false;
                    }
                }
                baseIndex += 6; // 移动到下一行
            }
            return true;
        }

        /// <summary>
        /// 比较两原料是否相等
        /// </summary>
        /// <param name="requiredIngredient"></param>
        /// <param name="actualIngredient"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        public static bool CompareIngredients(string requiredIngredient, string actualIngredient) {
            if (requiredIngredient == null) return actualIngredient == null;
            if (actualIngredient == null) return requiredIngredient == null;
            DecodeIEIngredient(requiredIngredient, out string craftingId, out int? data);
            DecodeIEIngredient(actualIngredient, out string craftingId2, out int? data2);
            bool requiredIE = requiredIngredient.Contains('-');
            bool actualIE = actualIngredient.Contains('-');
            if (!requiredIE
                && !actualIE) {
                //原版
                if (!data2.HasValue) {
                    throw new InvalidOperationException("Actual ingredient data not specified.");
                }
                if (craftingId == craftingId2) {
                    if (!data.HasValue) {
                        return true;
                    }
                    return data.Value == data2.Value;
                }
            }
            else if (requiredIE && actualIE) {
                //工业
                return craftingId == craftingId2;
            }
            return false;
        }

        /// <summary>
        /// 寻找符合条件的配方
        /// </summary>
        /// <param name="terrain"></param>
        /// <param name="ingredients"></param>
        /// <param name="heatLevel"></param>
        /// <param name="playerLevel"></param>
        /// <returns></returns>
        public static CraftingRecipe FindMatchingRecipe(SubsystemTerrain terrain, string[] ingredients, float heatLevel, float playerLevel) {
            CraftingRecipe craftingRecipe = null;
            Block[] blocks = BlocksManager.Blocks;
            for (int i = 0; i < blocks.Length; i++) {
                CraftingRecipe adHocCraftingRecipe = blocks[i].GetAdHocCraftingRecipe(terrain, ingredients, heatLevel, playerLevel);
                if (adHocCraftingRecipe != null
                    && MatchRecipe(adHocCraftingRecipe.Ingredients, ingredients)) {
                    craftingRecipe = adHocCraftingRecipe;
                    break;
                }
            }
            List<CraftingRecipe> cacheMatchedRecipes = [];
            if (craftingRecipe == null) {
                foreach (CraftingRecipe recipe in CraftingRecipesManager.Recipes) {
                    if (MatchRecipe(recipe.Ingredients, ingredients)) {
                        cacheMatchedRecipes.Add(recipe);
                    }
                }
            }
            if (cacheMatchedRecipes.Count > 0) {
                foreach (var cacheMatchedRecipe in cacheMatchedRecipes) {
                    if (cacheMatchedRecipe.RequiredHeatLevel >= 0) {//craftingRecipe.RequiredHeatLevel，不是熔炼就是合成
                        if (heatLevel < cacheMatchedRecipe.RequiredHeatLevel) {
                            craftingRecipe = (!(heatLevel > 0f)) ? new CraftingRecipe { Message = LanguageControl.Get(CraftingRecipesManager.fName, 0) } : new CraftingRecipe { Message = LanguageControl.Get(CraftingRecipesManager.fName, 1) };
                        }
                        else {
                            craftingRecipe = cacheMatchedRecipe;
                            break;
                        }
                    }
                    else {
                        if (heatLevel != cacheMatchedRecipe.RequiredHeatLevel) {
                            craftingRecipe = new CraftingRecipe { Message = "当前机器(设备)无法干这个活，找找合适的机器吧" };
                        }
						//else if (playerLevel < acheMatchedRecipe.RequiredPlayerLevel) {
						//    craftingRecipe = (!(cacheMatchedRecipe.RequiredHeatLevel > 0f)) ? new CraftingRecipe { Message = String.Format(LanguageControl.Get(CraftingRecipesManager.fName, 2), cacheMatchedRecipe.RequiredPlayerLevel) } : new CraftingRecipe { Message = String.Format(LanguageControl.Get(CraftingRecipesManager.fName, 3), cacheMatchedRecipe.RequiredPlayerLevel) };
						//}
						else
						{
                            craftingRecipe = cacheMatchedRecipe;
                            break;
                        }
                    }
                }
            }
            return craftingRecipe;
        }

        /// <summary>
        /// 通过方块实例与特殊值生成方块合成标识（用于合成）
        /// </summary>
        /// <param name="block"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string BlockToIngredient(int value) {
            Block block = BlocksManager.Blocks[Terrain.ExtractContents(value)];
            int data = Terrain.ExtractData(value);
            return block switch {
                Block n when n is BaseItemBlock => "I-" + ItemsBlockManager.GetItemCraftingID(value),
                Block n when n is BaseDeviceBlock => "D-" + DevicesBlockManager.GetDeviceCraftingID(value),
                Block n when n is BaseNormalBlock => "N-" + NormalBlockManager.GetNormalBlockCraftingID(value),
                Block n when n is BaseDamageableItemBlock => "H-" + DamageableItemManager.GetItemCraftingID(value),
                _ => block.GetCraftingId(value) + ":" + data.ToString(CultureInfo.InvariantCulture)
            };
        }

        /// <summary>
        /// 依据产物ID搜索配方
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static List<CraftingRecipe> SearchForRecipe(int value) {
            List<CraftingRecipe> recipes = new();
            //先遍历热量需求为分离器的配方
            foreach (var craftingRecipe in CraftingRecipesManager.Recipes.Where(x => x.RequiredHeatLevel == ComponentSifter.RequiredHeatLevel)) {
                string ingredient = IECraftingRecipesManager.BlockToIngredient(value);
                foreach (var requireIngredient in craftingRecipe.Ingredients) {
                    if (requireIngredient == null) continue;
                    if (IECraftingRecipesManager.CompareIngredients(requireIngredient, ingredient)) {
                        recipes.Add(craftingRecipe);
                        break;
                    }
                }
            }
            //再遍历原版的
            recipes.AddRange(CraftingRecipesManager.Recipes.Where((CraftingRecipe r) => r.ResultValue == value && r.ResultValue != 0 && r.RequiredHeatLevel != ComponentSifter.RequiredHeatLevel));

            return recipes;
        }
        #endregion

    }
}